package flexo.file;

import java.util.ArrayList;

import flexo.common.LogMsgType;

/**
 * A node of the file structure.
 * @author Miguel
 */
public class Node
{
	public String name;
	
	protected ArrayList<Property> properties;
	protected Node parent;
	protected ArrayList<Node> children;
	
	/**
	 * Initializes the node.
	 * @param name the name of the node.
	 */
	public Node(String name)
	{
		this.name = name;
		properties = new ArrayList<Property>();
		children = new ArrayList<Node>();
	}
	
	/**
	 * @return the parent node of this node.
	 */
	public Node getParent()
	{
		return parent;
	}
	
	/**
	 * @return the number of children nodes of this node.
	 */
	public int getChildrenCount()
	{
		return children.size();
	}
	
	/**
	 * @return the number of properties of this node.
	 */
	public int getPropertyCount()
	{
		return properties.size();
	}
	
	/**
	 * Returns a specific child node from this node.
	 * @param name the name of the node you want to find.
	 * @return a node, if it's found, or null.
	 */
	public Node getChild(String name)
	{
		for (Node n: children)
		{
			if (n.name.equals(name))
			{
				return n;
			}
		}
		
		flexo.Kernel.log("flexo.file.Node", "Child node named '"+name+"' not found", LogMsgType.WARNING);
		return null;
	}
	
	/**
	 * Retrieves a child node from a specific position inside the child list. 
	 */
	public Node getChild(int pos)
	{
		if (pos >= children.size() || pos < 0)
		{
			flexo.Kernel.log("flexo.file.Node", "Accesing child with position out of range", LogMsgType.WARNING);
			return null;
		}
		else
		{
			return children.get(pos);
		}
	}
	
	/**
	 * Adds a child node.
	 */
	public void addChild(Node node)
	{
		children.add(node);
	}
	
	/**
	 * Adds an empty child node.
	 * @param name the name of the node.
	 * @return the node itself, in case you want to set some of its properties.
	 */
	public Node addChild(String name)
	{
		Node n = new Node(name);
		children.add(n);
		return n;
	}
	
	/**
	 * Removes a child node.
	 * @param name the name of the node you want to remove.
	 */
	public void removeChild(String name)
	{
		for (Node n: children)	
		{
			if (n.name == name)
			{
				n.parent = null;
				children.remove(n);
			}
		}
	}
	
	/**
	 * Removes the node ONLY if it has a parent (it uses parent.removeChild method).
	 */
	public void remove()
	{
		if (parent != null)
		{
			parent.removeChild(name);
		}
	}
	
	/**
	 * Finds a specific child node recursively from this node.
	 * @param name the name of the node you want to find.
	 * @return a node, if it's found, or null.
	 */
	public Node findChild(String name)
	{
		if (this.name == name)
		{
			return this;
		}
		else
		{
			Node node = null;

			for (int i = 0; (i < children.size()) && (node == null); i++)
			{
				node = children.get(i).findChild(name);
			}
			return node;
		}
	}
	
	/**
	 * Retrieves a property.
	 * @param name the name of the property retrieved.
	 * @return the property itself.
	 */
	public Property getProperty(String name)
	{
		for (Property n: properties)
		{
			if (n.name.equals(name))
			{
				return n;
			}
		}
		flexo.Kernel.log("flexo.file.Node", "Property named '"+name+"' not found", LogMsgType.WARNING);
		return null;
	}
	
	protected Property getOrCreateProperty(String name)
	{
		Property p = null;
		
		for (Property n: properties)
		{
			if (n.name.equals(name))
			{
				p = n;
			}
		}
		
		if (p == null)
		{
			p = new Property(name,"");
			properties.add(p);
		}
		return p;
	}
	
	/**
	 * Gets a property in a specified position, in case you want to iterate.
	 */
	public Property getProperty(int pos)
	{
		if (pos >= this.properties.size() || pos < 0)
		{
			flexo.Kernel.log("flexo.file.Node", "Accesing property with position out of range", LogMsgType.WARNING);
			return null;
		}
		else
		{
			return properties.get(pos);
		}		
	}
	
	/**
	 * Removes a property with a specified name.
	 */
	public void removeProperty(String name)
	{
		for (int i = 0; i < properties.size(); i++)
		{
			if (properties.get(i).name == name)
			{
				properties.remove(i);
				return;
			}
		}
	}
	
	/**
	 * Checks if a property already exists.
	 * @param name the name of the property.
	 * @return true if a property with that name, false otherwise.
	 */
	public boolean existsProperty(String name)
	{
		for (Property n: properties)
		{
			if (n.name.equals(name))
			{
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Sets a property's value. If the property doesn't exists, it's
	 * automatically created.
	 */
	public void setProperty(String name, String value)
	{
		Property p = getOrCreateProperty(name);
		p.setStringValue(value);		
	}
	
	/**
	 * Sets a property's value. If the property doesn't exists, it's
	 * automatically created.
	 */
	public void setProperty(String name, char value)
	{
		Property p = getOrCreateProperty(name);
		p.setCharValue(value);
	}
	
	/**
	 * Sets a property's value. If the property doesn't exists, it's
	 * automatically created.
	 */
	public void setProperty(String name, int value)
	{
		Property p = getOrCreateProperty(name);
		p.setIntValue(value);
	}
	
	/**
	 * Sets a property's value. If the property doesn't exists, it's
	 * automatically created.
	 */
	public void setProperty(String name, float value)
	{
		Property p = getOrCreateProperty(name);
		p.setFloatValue(value);
	}
	
	/**
	 * Sets a property's value. If the property doesn't exists, it's
	 * automatically created.
	 */
	public void setProperty(String name, boolean value)
	{
		Property p = getOrCreateProperty(name);
		p.setBooleanValue(value);
	}
	
	/**
	 * Returns all the information of the node: name, children nodes and properties.
	 * String is formated as console output.
	 */
	public String toString()
	{
		String result = "Name: " + name + "\n";
		result += "Properties: ";
		
		if (properties.size() == 0)
		{
			result += "-" + "\n";
		}
		else
		{
			result += "\n";
		}
		for (Property p: properties)
		{
			result += "\t" + p.toString() + "\n";
		}
		
		result += "Children: ";
		if (children.size() == 0)
		{
			result += "-" + "\n";
		}
		else
		{
			result += "\n";
		}
		
		for (Node c: children)
		{
			result += c.toString() + "\n";
		}
		return result;
	}
}
